最近闲来无事,饭也吃饱了。
ps:我一般就是记个随笔,不要当作教程来看,我会比较详细的记录研究过程以及遇到的问题,所以,可能中间有些东西会写错了,后面修改,也可能写错了我自己也不知道,欢迎指正。如果要动手,尽量看完再动手,默认所有人都有最基本的判断力。
准备搞搞研究研究破解相关的东西,首先声明,只做研究,不做坏坏的事情。
首先呢,了解到,破解和注入啥的有很大的相关性。
经过查资料,发现只要b把自己注入到其他进程中,就能做很多的事情。
那么下面,就尝试着写个dll,注入一下试试看。
vs新建工程,选择动态库,写上以下代码,没几行就不贴代码了。

然后尝试一下,怎么注入呢,下载个工具试试效果。工具直接搜索注入工具就有了。试试注入到vs中看看效果。


从下面的提示图标可以看出来,这个提示框已经是vs进程的了,图标都变了。
但是这个工具感觉不好用,我dll修改东西的时候老是提示我,文件被占用,又无法卸载dll。
那个注出按钮一点就崩溃。所以咱们自己写个注入工具吧。
经过百度和到处ctrl+c以及之前研究键盘hook新建一个控制台程序,代码如下。
// mxInject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <windows.h>
#include"tlhelp32.h"
#include<tchar.h>
#define DLLPath "DLL的存放位置"
int Inject(int pid)
{
HANDLE hprocess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
if (!hprocess)
{
std::cout << "can not get handle" << std::endl;
return 1;
}
SIZE_T PathSize = (strlen(DLLPath) + 1) * sizeof(TCHAR);
LPVOID StartAddress = VirtualAllocEx(hprocess, NULL, PathSize, MEM_COMMIT, PAGE_READWRITE);
if (!StartAddress)
{
std::cout << "开辟内存失败" << std::endl;
return 1;
}
if (!WriteProcessMemory(hprocess, StartAddress, DLLPath, PathSize, NULL))
{
std::cout << "无法写入DLL路径" << std::endl;
return 1;
}
PTHREAD_START_ROUTINE pfnStartAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (!pfnStartAddress)
{
std::cout << "无法获取函数地址" << std::endl;
return 1;
}
HANDLE hThread = CreateRemoteThreadEx(hprocess, NULL, NULL, pfnStartAddress, StartAddress, NULL, NULL, NULL);
if (!hThread)
{
std::cout << "创建线程失败" << std::endl;
return 1;
}
//WaitForSingleObject(hThread, INFINITE);//等待DLL结束
std::cout << "注入成功!\n";
CloseHandle(hThread);
CloseHandle(hprocess);
return 0;
}
BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
{
BOOL bMore = FALSE, bFound = FALSE;
HANDLE hSnapshot, hProcess, hThread;
HMODULE hModule = NULL;
MODULEENTRY32 me = { sizeof(me) };
LPTHREAD_START_ROUTINE pThreadProc;
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
bMore = Module32First(hSnapshot, &me);
for (; bMore; bMore = Module32Next(hSnapshot, &me))
{
if (!_tcsicmp((LPCTSTR)me.szModule, szDllName) || !_tcsicmp((LPCTSTR)me.szExePath, szDllName))
{
bFound = TRUE;
break;
}
}
if (!bFound)
{
CloseHandle(hSnapshot);
return FALSE;
}
if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
{
_tprintf("OpenProcess(%d) failed!!! [%d]\n,", dwPID, GetLastError());
return FALSE;
}
hModule = GetModuleHandle("Kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
CloseHandle(hSnapshot);
return TRUE;
}
LRESULT CALLBACK CallBackProc_keyboard(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode != HC_ACTION)
{
return CallNextHookEx(0, nCode, wParam, lParam);
}
if (wParam == WM_KEYDOWN || wParam == WM_KEYUP)
{
PKBDLLHOOKSTRUCT hookStruct = (PKBDLLHOOKSTRUCT)lParam;
if (hookStruct->vkCode == VK_F3)
{
if (wParam == WM_KEYDOWN)
{
HWND hwnd = GetForegroundWindow();
DWORD dwPID,dwTid;
dwTid = GetWindowThreadProcessId(hwnd, &dwPID);
printf("开始注入%d\n", dwPID);
Inject(dwPID);
printf("注入%d结束\n", dwPID);
}
return 1;
}
if (hookStruct->vkCode == VK_F4)
{
if (wParam == WM_KEYDOWN)
{
HWND hwnd = GetForegroundWindow();
DWORD dwPID, dwTid;
dwTid = GetWindowThreadProcessId(hwnd, &dwPID);
printf("开始卸载dll%d\n", dwPID);
EjectDll(dwPID,"remotecall.dll");
printf("dll卸载%d结束\n", dwPID);
}
return 1;
}
}
return CallNextHookEx(0, nCode, wParam, lParam);
}
int main()
{
SetWindowsHookEx(WH_KEYBOARD_LL, CallBackProc_keyboard, GetModuleHandle(nullptr), 0);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
F3注入 F4卸载。测试一下。


完全没有问题。既然已经可以打入敌人内部了,那么应该可以修改点内存啥的,就是怎么找内存呢。
从网上找资料,发现可以根据偏移来查找。
为了测试方便,咱们写个程序用来破解吧。
vs新建MFC工程,基于对话框,拖个控件。

写个onok

void CloginDlg::OnBnClickedOk()
{
CString str;
GetDlgItemText(IDC_EDIT1, str);
if (str != _T("123"))
{
MessageBox("密码错误");
return ;
}
// TODO: 在此添加控件通知处理程序代码
CDialogEx::OnOK();
}
运行效果如下

然后试试能不能破解。下载个ce工具,这个全名叫Cheat Engine 直接百度就可以了。

扫描一下,发现不是绿色的,人家说绿色的才行。而且这个好像也不准。忽然发现代码中用的是字符串,这里用的是数值,修改成字符串再试试,

搜索出来两个,确认框点掉,发现就剩下一个了。
说明第二个就是咱们申请的临时变量。CString str;
第一个应该是控件中保存数据的变量,测试一下。

修改输入框内容,发现这个就跟着变化,这么就说明,这个变量是控件的变量,用来保存控件数据的,不过这好像也没啥用啊。需要有固定偏移,咱们用的都是动态申请的,所以搞个全局变量应该就好了,试试看。增加全局变量int m_nCount;并修改代码,记录密码错误的次数。

点击几次确定,然后搜索(别忘记了,修改成4字节的,因为这次咱们用的是int)

太多了,再点两下试试看。

这次就只剩下1个了,并且是绿色的

再点击两下,发现确实变化了。

那么记录下这个地址login.exe+0xBB3B58下面去修改dll代码
int OnInit()
{
MessageBox(0, "成功潜入敌人内部!", "报告主人", NULL);
HANDLE hand = GetModuleHandle("login.exe");
if (!hand)
{
MessageBox(0, "查找主句柄失败!", "报告主人", NULL);
return 0;
}
int noffset = 0XBB3B58;
int* pCount = (int*)((DWORD)hand + noffset);
*pCount = 15;
return 0;
}
然后再F3注入试试看。可以看到这个值变成15了。

点击确定,也提示的16次

到这里第一步算是完成了,我们实现了进入敌人内部,并修改敌人的内存数据。
Comments | NOTHING